# Test mechanism for Pearson algorithm implementation for collaborative filtering
# Author: Szymon Janikowski

# Init
# -----------------------------------------------

# Include prediction algorithm file
source("neighbours_time.r");

# Add tcltk library for progress bar
library(tcltk)

# Functions

# Wrapper for counting prediction. Needed to enable calling by 'apply'.
# Changes status of progress bar
# ---------------------------------------------------------------------------
# Parameters
# testcase - one row of test-set
# base - training data frame
# theta - theta similarity treshold
# v - similarity exponent
# k - time window growth factor
# T - initial time window width
# pb - progress bar
# ---------------------------------------------------------------------------
# Function called automaticaly during execution of tests
# ---------------------------------------------------------------------------		
predict_wrapper <- function(testcase,base,theta,v,k,T,pb)
{
	#Advance progress bar
	pb.current <<- pb.current + 1;
	setTkProgressBar(pb, pb.current, label=paste( round(pb.current/pb.total*100, 2),"% done"));
	
	#Calling predict with appropriate parameters
	prediction <-predict(testcase["user"],testcase["item"], testcase["timestamp"],base,theta,v,k,T);
	return(prediction);
}
	

# Function performing test for single vector of parameters
# ---------------------------------------------------------------------------
# Parameters
# testrow - one row of a testsuite
# output file - output file in csv ';' separated format
	
perform.test<-function(testrow,output.file)
{
	# Parameters: theta treshold, v exponent, k base and T width for He - Wu TBCF
	theta <- as.numeric(testrow["theta"]);
	v <- as.numeric(testrow["v"]);
	k <- as.numeric(testrow["k"]);
	T <- as.numeric(testrow["T"]);
	
	# Read base and test datasets according to testrow
	base <- read.csv2(paste(testrow["data.set"],"\\",testrow["base.file"],sep=""));
	test <- read.csv2(paste(testrow["data.set"],"\\",testrow["test.file"],sep=""));
		
	# Eliminating those items for which it's impossible to find prediction (there is no data in base)
	test <- test[test$item %in% base$item,];

	# Progress bar initialisation
	pb.total <<- nrow(test);
	pb.current <<- 0;
	pb <- tkProgressBar(title = (testrow["test.id"]), min = 0,
                    max = pb.total, width = 300)
	
	#Aplly test for every pair user item from the test set 
	predictions <- apply(test,1, predict_wrapper,base,theta,v,k,T,pb)
	
	# Close progress bar
	close(pb)

	# Compute measures
	mae <- compute.mae(test,predictions);
	round.mae <- compute.round.mae(test,predictions);
	measures <- c(mae,round.mae);

	# Write result to output file
	test.result <- t(c(testrow,measures,recursive=TRUE))
	write.table(test.result,output.file,row.names=FALSE,col.names=FALSE,sep=";",append=TRUE);
	
	# Sample result vector - for binding outside

	result <- c(mae,round.mae);
	return (result);
}

# Mean Absolute Error computing function
# ---------------------------------------------
# Parameters:
# test - test data set
# predictions - a vector of predictions
# ---------------------------------------------
# Note: normalized <0;1>
# ---------------------------------------------
compute.mae <- function(test,predictions)
{
	print("Test",quote=FALSE);
	print(test);
	print("Predictions",quote=FALSE);
	print(predictions);
	result <- mean(abs(test$rating - predictions)/5);
	return (result);
}

# Round Mean Absolute Error computing function
# ---------------------------------------------
# Parameters:
# test - test data set
# predictions - a vector of predictions
# ---------------------------------------------
# Note: normalized <0;1>
# ---------------------------------------------
compute.round.mae <- function(test,predictions)
{
	result <- mean(abs(test$rating - round(predictions))/5);
	return (result);
}

# Function for performing a suite of tests
# ---------------------------------------------------------------------------
# Parameters:
# test.file - a csv ';' separated file containing test rows
# output.file - a csv ';' separated file with extra results columns
# ---------------------------------------------------------------------------
# Function called automaticaly during execution of tests
# ---------------------------------------------------------------------------
perform.tests <- function(test.file, output.file)
{
	# Read and print set of tests
	test.set <- read.csv2(test.file);
	print("Performing set of tests:",quote=FALSE);
	print(test.set);

	# Write output header and pass the file for further computation
	measure.names <- c("MAE","round MAE");
	header <- t(c(names(test.set),measure.names,recursive=TRUE));

	write.table(header,output.file,row.names=FALSE,col.names=FALSE,sep=";");

	# Compute measures
	measures <- t(apply(test.set, 1, perform.test, output.file));
	
	# Compute results
	# result <- cbind(test.set,measures);
	
	# Write result to file
	# write.csv2(result,output.file,row.names=FALSE);

	# Return the result
	return(measures);
}

# Simple random sample generating function
# --------------------------------------------------------------
# test.file - input file for sampling, in csv ';' separated
# sample.size - how many test samples should be picked
# output.file - output file to write the test sequence to
# --------------------------------------------------------------
generate.sample <- function(test.file, sample.size, output.file)
{
	# Read from test.file
	data <- read.csv2(test.file);
	data.sample <- data[sample(length(data$user),sample.size),];
	write.csv2(data.sample,output.file,row.names=FALSE);
	return(0);
}
